# -*- coding: utf-8 -*-
"""
DTM验证工具 - 可视化模块
负责三维图形生成、图表绘制和结果保存
"""

import os
import matplotlib

matplotlib.use("Agg")  # 设置为非GUI后端，避免GUI问题
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import numpy as np
from typing import Optional, Dict, Any, List
from config_manager import Constants, ProjectConfig


class DTMVisualizer:
    """DTM三维可视化器"""

    def __init__(self, project_config: Optional[ProjectConfig] = None):
        self.project_config = project_config or ProjectConfig()
        self.constants = Constants()

        # 确保中文字体设置
        self._setup_chinese_fonts()

    def _setup_chinese_fonts(self):
        """设置中文字体支持"""
        # 尝试多种中文字体，解决上标字符问题
        matplotlib.rcParams["font.sans-serif"] = [
            "Microsoft YaHei",
            "SimHei",
            "DejaVu Sans",
            "Arial Unicode MS",
        ]
        matplotlib.rcParams["axes.unicode_minus"] = False
        # 设置字体回退机制
        matplotlib.rcParams["font.serif"] = ["Times New Roman", "SimSun"]

    def create_3d_visualization(
        self,
        triangles: np.ndarray,
        analysis_result: dict,
        output_path: str,
        title: Optional[str] = None,
    ) -> str:
        """
        创建三维可视化图像

        Args:
            triangles: 三角形数组
            analysis_result: 几何分析结果
            output_path: 输出文件路径
            title: 图像标题

        Returns:
            str: 输出文件的完整路径
        """
        try:
            # 创建图形和3D子图
            fig = plt.figure(figsize=self.project_config.figure_size)
            ax = fig.add_subplot(111, projection="3d")

            # 绘制三角形网格
            self._plot_triangular_mesh(ax, triangles)

            # 设置坐标轴
            self._setup_axes(ax, triangles, title or "DTM 三维可视化")

            # 添加统计信息文本
            self._add_statistics_text(ax, analysis_result)

            # 可选：添加三角形标注
            if len(triangles) <= 50:  # 只在三角形数量较少时添加标注
                self._add_triangle_annotations(
                    ax, triangles, analysis_result["base_height"]
                )

            # 保存图像
            plt.savefig(
                output_path,
                dpi=self.project_config.figure_dpi,
                bbox_inches="tight",
                facecolor="white",
            )
            plt.close()

            return output_path

        except Exception as e:
            plt.close()
            raise ValueError(f"创建三维可视化失败: {str(e)}")

    def _plot_triangular_mesh(self, ax, triangles: np.ndarray):
        """绘制三角形网格"""
        # 创建三维多边形集合
        poly_collection = Poly3DCollection(
            triangles, alpha=0.7, edgecolor="black", facecolor="cyan", linewidths=0.5
        )
        ax.add_collection3d(poly_collection)

    def _setup_axes(self, ax, triangles: np.ndarray, title: str):
        """设置坐标轴属性"""
        # 计算坐标范围
        all_points = np.concatenate(triangles, axis=0)
        x_min, x_max = all_points[:, 0].min(), all_points[:, 0].max()
        y_min, y_max = all_points[:, 1].min(), all_points[:, 1].max()
        z_min, z_max = all_points[:, 2].min(), all_points[:, 2].max()

        # 设置坐标轴范围
        ax.set_xlim(x_min, x_max)
        ax.set_ylim(y_min, y_max)
        ax.set_zlim(z_min, z_max)

        # 设置标签和标题
        ax.set_xlabel("X坐标 (m)")
        ax.set_ylabel("Y坐标 (m)")
        ax.set_zlabel("高程 (m)")
        ax.set_title(title)

        # 设置等比例坐标轴
        ax.set_box_aspect([x_max - x_min, y_max - y_min, z_max - z_min])

        # 设置刻度
        self._setup_axis_ticks(ax, x_min, x_max, y_min, y_max, z_min, z_max)

    def _setup_axis_ticks(
        self,
        ax,
        x_min: float,
        x_max: float,
        y_min: float,
        y_max: float,
        z_min: float,
        z_max: float,
    ):
        """设置坐标轴刻度"""
        # 计算合适的刻度间隔
        x_interval = self._calculate_tick_interval(x_max - x_min)
        y_interval = self._calculate_tick_interval(y_max - y_min)
        z_interval = self._calculate_tick_interval(z_max - z_min)

        # 设置刻度
        ax.set_xticks(
            np.arange(
                np.floor(x_min / x_interval) * x_interval,
                np.ceil(x_max / x_interval) * x_interval + x_interval,
                x_interval,
            )
        )
        ax.set_yticks(
            np.arange(
                np.floor(y_min / y_interval) * y_interval,
                np.ceil(y_max / y_interval) * y_interval + y_interval,
                y_interval,
            )
        )
        ax.set_zticks(
            np.arange(
                np.floor(z_min / z_interval) * z_interval,
                np.ceil(z_max / z_interval) * z_interval + z_interval,
                z_interval,
            )
        )

    def _calculate_tick_interval(self, range_value: float) -> float:
        """计算合适的刻度间隔"""
        if range_value <= 0:
            return 1.0

        # 计算数量级
        magnitude = 10 ** np.floor(np.log10(range_value))

        # 选择合适的间隔
        normalized = range_value / magnitude
        if normalized <= 2:
            return magnitude * 0.5
        elif normalized <= 5:
            return magnitude
        else:
            return magnitude * 2

    def _add_statistics_text(self, ax, analysis_result: dict):
        """添加统计信息文本"""
        volume_info = analysis_result["volume_info"]
        surface_areas = analysis_result["surface_areas"]
        vertex_stats = analysis_result["vertex_statistics"]

        stats_text = (
            f"顶点数量: {analysis_result['vertex_count']}\n"
            f"三角形数量: {analysis_result['triangle_count']}\n"
            f"基准高程: {analysis_result['base_height']:.{self.constants.HEIGHT_PRECISION}f} m\n"
            f"高程范围: {vertex_stats['z_range'][0]:.{self.constants.HEIGHT_PRECISION}f} ~ "
            f"{vertex_stats['z_range'][1]:.{self.constants.HEIGHT_PRECISION}f} m\n"
            f"三维表面积: {surface_areas['total_3d_area']:.{self.constants.AREA_PRECISION}f} m²\n"
            f"投影面积: {surface_areas['total_projection_area']:.{self.constants.AREA_PRECISION}f} m²\n"
            f"有效投影面积: {surface_areas['effective_projection_area']:.{self.constants.AREA_PRECISION}f} m²\n"
            f"总体积: {volume_info['total_absolute_volume']:.{self.constants.VOLUME_PRECISION}f} m³\n"
            f"净体积: {volume_info['net_volume']:.{self.constants.VOLUME_PRECISION}f} m³"
        )

        # 在图形左上角添加文本框
        ax.text2D(
            0.05,
            0.95,
            stats_text,
            transform=ax.transAxes,
            fontsize=9,
            verticalalignment="top",
            horizontalalignment="left",
            bbox=dict(boxstyle="round,pad=0.5", facecolor="white", alpha=0.8),
        )

    def _add_triangle_annotations(self, ax, triangles: np.ndarray, base_height: float):
        """为三角形添加面积和体积标注（仅在三角形数量较少时使用）"""
        from geometry_calculator import GeometryCalculator

        calc = GeometryCalculator()

        for i, triangle in enumerate(triangles):
            # 计算三角形属性
            area_3d = calc.calculate_triangle_area_3d(triangle)
            volume = calc.calculate_triangle_volume(triangle, base_height)
            centroid = calc.calculate_triangle_centroid(triangle)

            # 添加标注
            ax.text(
                centroid[0],
                centroid[1],
                centroid[2],
                f"#{i+1}\nA:{area_3d:.2f}\nV:{volume:.2f}",
                color="red",
                fontsize=6,
                ha="center",
                va="center",
            )


class ReportGenerator:
    """报告生成器"""

    def __init__(self, output_dir: str):
        self.output_dir = output_dir
        os.makedirs(output_dir, exist_ok=True)

    def generate_summary_report(
        self, batch_results: List[dict], output_filename: Optional[str] = None
    ) -> str:
        """
        生成批量处理汇总报告

        Args:
            batch_results: 批量处理结果列表
            output_filename: 输出文件名（可选）

        Returns:
            str: 报告文件路径
        """
        import pandas as pd
        from datetime import datetime

        # 准备报告数据
        report_data = []

        for result in batch_results:
            if result.get("success", False) and "analysis_result" in result:
                analysis = result["analysis_result"]
                volume_info = analysis["volume_info"]
                surface_areas = analysis["surface_areas"]
                vertex_stats = analysis["vertex_statistics"]

                report_data.append(
                    {
                        "文件名": os.path.basename(result["file_path"]),
                        "基准高程(m)": analysis["base_height"],
                        "顶点数量": analysis["vertex_count"],
                        "三角形数量": analysis["triangle_count"],
                        "总体积(m³)": round(
                            volume_info["total_absolute_volume"],
                            Constants.VOLUME_PRECISION,
                        ),
                        "净体积(m³)": round(
                            volume_info["net_volume"], Constants.VOLUME_PRECISION
                        ),
                        "正体积(m³)": round(
                            volume_info["positive_volume"], Constants.VOLUME_PRECISION
                        ),
                        "负体积(m³)": round(
                            volume_info["negative_volume"], Constants.VOLUME_PRECISION
                        ),
                        "三维表面积(m²)": round(
                            surface_areas["total_3d_area"], Constants.AREA_PRECISION
                        ),
                        "投影面积(m²)": round(
                            surface_areas["total_projection_area"],
                            Constants.AREA_PRECISION,
                        ),
                        "有效投影面积(m²)": round(
                            surface_areas["effective_projection_area"],
                            Constants.AREA_PRECISION,
                        ),
                        "最小高程(m)": round(
                            vertex_stats["z_range"][0], Constants.HEIGHT_PRECISION
                        ),
                        "最大高程(m)": round(
                            vertex_stats["z_range"][1], Constants.HEIGHT_PRECISION
                        ),
                        "处理状态": "成功",
                    }
                )
            else:
                report_data.append(
                    {
                        "文件名": os.path.basename(result["file_path"]),
                        "处理状态": f"失败: {result.get('error', '未知错误')}",
                    }
                )

        # 创建DataFrame
        df = pd.DataFrame(report_data)

        # 添加汇总行
        if report_data:
            successful_results = [d for d in report_data if d.get("处理状态") == "成功"]
            if successful_results:
                summary_row = {
                    "文件名": "=== 汇总统计 ===",
                    "基准高程(m)": "",
                    "顶点数量": sum(d.get("顶点数量", 0) for d in successful_results),
                    "三角形数量": sum(
                        d.get("三角形数量", 0) for d in successful_results
                    ),
                    "总体积(m³)": sum(
                        d.get("总体积(m³)", 0) for d in successful_results
                    ),
                    "净体积(m³)": sum(
                        d.get("净体积(m³)", 0) for d in successful_results
                    ),
                    "正体积(m³)": sum(
                        d.get("正体积(m³)", 0) for d in successful_results
                    ),
                    "负体积(m³)": sum(
                        d.get("负体积(m³)", 0) for d in successful_results
                    ),
                    "三维表面积(m²)": sum(
                        d.get("三维表面积(m²)", 0) for d in successful_results
                    ),
                    "投影面积(m²)": sum(
                        d.get("投影面积(m²)", 0) for d in successful_results
                    ),
                    "有效投影面积(m²)": sum(
                        d.get("有效投影面积(m²)", 0) for d in successful_results
                    ),
                    "处理状态": f"成功: {len(successful_results)}, 失败: {len(report_data) - len(successful_results)}",
                }
                df = pd.concat([df, pd.DataFrame([summary_row])], ignore_index=True)

        # 生成文件名
        if not output_filename:
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            output_filename = f"DTM处理报告_{timestamp}.xlsx"

        # 保存报告
        report_path = os.path.join(self.output_dir, output_filename)

        # 使用ExcelWriter来设置格式
        with pd.ExcelWriter(report_path, engine="openpyxl") as writer:
            df.to_excel(writer, sheet_name="处理报告", index=False)

            # 获取工作表来设置格式
            worksheet = writer.sheets["处理报告"]

            # 设置列宽
            for column in worksheet.columns:
                max_length = 0
                column_letter = column[0].column_letter
                for cell in column:
                    try:
                        if len(str(cell.value)) > max_length:
                            max_length = len(str(cell.value))
                    except:
                        pass
                adjusted_width = min(max_length + 2, 50)
                worksheet.column_dimensions[column_letter].width = adjusted_width

        return report_path

    def generate_processing_log(
        self, log_messages: List[str], output_filename: Optional[str] = None
    ) -> str:
        """
        生成处理日志文件

        Args:
            log_messages: 日志消息列表
            output_filename: 输出文件名（可选）

        Returns:
            str: 日志文件路径
        """
        from datetime import datetime

        if not output_filename:
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            output_filename = f"处理日志_{timestamp}.txt"

        log_path = os.path.join(self.output_dir, output_filename)

        with open(log_path, "w", encoding="utf-8") as f:
            f.write(f"DTM批量验证处理日志\n")
            f.write(f"生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
            f.write("=" * 50 + "\n\n")

            for message in log_messages:
                f.write(f"{message}\n")

        return log_path


class VisualizationManager:
    """可视化管理器 - 统一管理所有可视化操作"""

    def __init__(self, output_dir: str):
        self.output_dir = output_dir
        self.visualizer = DTMVisualizer()
        self.report_generator = ReportGenerator(output_dir)
        os.makedirs(output_dir, exist_ok=True)

    def process_visualization_batch(self, analysis_results: List[dict]) -> List[str]:
        """
        批量处理可视化任务

        Args:
            analysis_results: 分析结果列表

        Returns:
            List[str]: 生成的图像文件路径列表
        """
        generated_files = []

        for result in analysis_results:
            if result.get("success", False) and "analysis_result" in result:
                try:
                    # 生成输出文件名
                    base_name = os.path.splitext(os.path.basename(result["file_path"]))[
                        0
                    ]
                    output_path = os.path.join(self.output_dir, f"{base_name}_3d.png")

                    # 创建可视化
                    analysis = result["analysis_result"]
                    generated_path = self.visualizer.create_3d_visualization(
                        triangles=analysis["triangles"],
                        analysis_result=analysis,
                        output_path=output_path,
                        title=f"{base_name} 三维可视化",
                    )

                    generated_files.append(generated_path)

                except Exception as e:
                    print(f"生成可视化失败 {result['file_path']}: {str(e)}")

        return generated_files

    def generate_comprehensive_report(
        self, batch_results: List[dict], log_messages: List[str]
    ) -> Dict[str, str]:
        """
        生成综合报告（包括Excel报告和日志文件）

        Args:
            batch_results: 批量处理结果
            log_messages: 日志消息列表

        Returns:
            Dict[str, str]: 生成的文件路径字典
        """
        generated_files = {}

        try:
            # 生成Excel报告
            excel_report = self.report_generator.generate_summary_report(batch_results)
            generated_files["excel_report"] = excel_report
        except Exception as e:
            print(f"生成Excel报告失败: {str(e)}")

        try:
            # 生成日志文件
            log_file = self.report_generator.generate_processing_log(log_messages)
            generated_files["log_file"] = log_file
        except Exception as e:
            print(f"生成日志文件失败: {str(e)}")

        return generated_files
